From b6be4038be71028900cc112b76719e93b7a12bfd Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 22 Oct 2014 22:18:49 -0700 Subject: [PATCH] Restructure resolve/lockfile ops Move functionality from cargo_fetch and cargo_generate_lockfile into dedicated lockfile/resolve modules. The plan is to expand the resolve module significantly to deal with the lockfile that's loaded. --- src/cargo/ops/cargo_compile.rs | 2 +- src/cargo/ops/cargo_fetch.rs | 30 +------ src/cargo/ops/cargo_generate_lockfile.rs | 101 ++--------------------- src/cargo/ops/lockfile.rs | 101 +++++++++++++++++++++++ src/cargo/ops/mod.rs | 23 ++++-- src/cargo/ops/resolve.rs | 56 +++++++++++++ 6 files changed, 181 insertions(+), 132 deletions(-) create mode 100644 src/cargo/ops/lockfile.rs create mode 100644 src/cargo/ops/resolve.rs diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index feff4fc5e..ef4464751 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -90,7 +90,7 @@ pub fn compile_pkg(package: &Package, options: &mut CompileOptions) // First, resolve the package's *listed* dependencies, as well as // downloading and updating all remotes and such. - try!(ops::resolve_and_fetch(&mut registry, package)); + try!(ops::resolve_pkg(&mut registry, package)); // Second, resolve with precisely what we're doing. Filter out // transitive dependencies if necessary, specify features, handle diff --git a/src/cargo/ops/cargo_fetch.rs b/src/cargo/ops/cargo_fetch.rs index e4581f627..73d52a3ab 100644 --- a/src/cargo/ops/cargo_fetch.rs +++ b/src/cargo/ops/cargo_fetch.rs @@ -16,34 +16,6 @@ pub fn fetch(manifest_path: &Path, let mut config = try!(Config::new(shell, None, None)); let mut registry = PackageRegistry::new(&mut config); - try!(resolve_and_fetch(&mut registry, &package)); + try!(ops::resolve_pkg(&mut registry, &package)); Ok(()) } - -/// Finds all the packages required to compile the specified `Package`, -/// and loads them in the `PackageRegistry`. -/// -/// Also write the `Cargo.lock` file with the results. -pub fn resolve_and_fetch(registry: &mut PackageRegistry, package: &Package) - -> CargoResult { - let _p = profile::start("resolve and fetch..."); - - let lockfile = package.get_manifest_path().dir_path().join("Cargo.lock"); - let source_id = package.get_package_id().get_source_id(); - let previous_resolve = try!(ops::load_lockfile(&lockfile, source_id)); - let sources = match previous_resolve { - Some(ref r) => r.iter().map(|p| p.get_source_id().clone()).collect(), - None => vec![package.get_package_id().get_source_id().clone()], - }; - try!(registry.add_sources(sources.as_slice())); - - let mut resolved = try!(resolver::resolve(package.get_summary(), - resolver::ResolveEverything, - registry)); - match previous_resolve { - Some(ref prev) => resolved.copy_metadata(prev), - None => {} - } - try!(ops::write_resolve(package, &resolved)); - Ok(resolved) -} diff --git a/src/cargo/ops/cargo_generate_lockfile.rs b/src/cargo/ops/cargo_generate_lockfile.rs index 6d518380b..f789c3360 100644 --- a/src/cargo/ops/cargo_generate_lockfile.rs +++ b/src/cargo/ops/cargo_generate_lockfile.rs @@ -4,13 +4,14 @@ use std::io::File; use serialize::{Encodable, Decodable}; use toml::{mod, Encoder}; +use core::PackageId; use core::registry::PackageRegistry; use core::{MultiShell, Source, Resolve, resolver, Package, SourceId}; -use core::PackageId; +use ops; use sources::{PathSource}; use util::config::{Config}; -use util::{CargoResult, human}; use util::toml as cargo_toml; +use util::{CargoResult, human}; pub struct UpdateOptions<'a> { pub shell: &'a mut MultiShell<'a>, @@ -26,14 +27,9 @@ pub fn generate_lockfile(manifest_path: &Path, try!(source.update()); let package = try!(source.get_root_package()); let mut config = try!(Config::new(shell, None, None)); - - let resolve = { - let mut registry = PackageRegistry::new(&mut config); - try!(resolver::resolve(package.get_summary(), - resolver::ResolveEverything, - &mut registry)) - }; - try!(write_resolve(&package, &resolve)); + let mut registry = PackageRegistry::new(&mut config); + let resolve = try!(ops::resolve_with_previous(&mut registry, &package, None)); + try!(ops::write_pkg_lockfile(&package, &resolve)); Ok(()) } @@ -43,9 +39,7 @@ pub fn update_lockfile(manifest_path: &Path, try!(source.update()); let package = try!(source.get_root_package()); - let lockfile = package.get_root().join("Cargo.lock"); - let source_id = package.get_package_id().get_source_id(); - let previous_resolve = match try!(load_lockfile(&lockfile, source_id)) { + let previous_resolve = match try!(ops::load_pkg_lockfile(&package)) { Some(resolve) => resolve, None => return Err(human("A Cargo.lock must exist before it is updated")) }; @@ -89,7 +83,7 @@ pub fn update_lockfile(manifest_path: &Path, resolver::ResolveEverything, &mut registry)); resolve.copy_metadata(&previous_resolve); - try!(write_resolve(&package, &resolve)); + try!(ops::write_pkg_lockfile(&package, &resolve)); return Ok(()); fn fill_with_deps<'a>(resolve: &'a Resolve, dep: &'a PackageId, @@ -107,82 +101,3 @@ pub fn update_lockfile(manifest_path: &Path, } } } - -pub fn load_lockfile(path: &Path, sid: &SourceId) -> CargoResult> { - // If there is no lockfile, return none. - let mut f = match File::open(path) { - Ok(f) => f, - Err(_) => return Ok(None) - }; - - let s = try!(f.read_to_string()); - - let table = toml::Table(try!(cargo_toml::parse(s.as_slice(), path))); - let mut d = toml::Decoder::new(table); - let v: resolver::EncodableResolve = Decodable::decode(&mut d).unwrap(); - Ok(Some(try!(v.to_resolve(sid)))) -} - -pub fn write_resolve(pkg: &Package, resolve: &Resolve) -> CargoResult<()> { - let loc = pkg.get_root().join("Cargo.lock"); - - let mut e = Encoder::new(); - resolve.encode(&mut e).unwrap(); - - let mut out = String::new(); - - // Note that we do not use e.toml.to_string() as we want to control the - // exact format the toml is in to ensure pretty diffs between updates to the - // lockfile. - let root = e.toml.find(&"root".to_string()).unwrap(); - - out.push_str("[root]\n"); - emit_package(root.as_table().unwrap(), &mut out); - - let deps = e.toml.find(&"package".to_string()).unwrap().as_slice().unwrap(); - for dep in deps.iter() { - let dep = dep.as_table().unwrap(); - - out.push_str("[[package]]\n"); - emit_package(dep, &mut out); - } - - match e.toml.find(&"metadata".to_string()) { - Some(metadata) => { - out.push_str("[metadata]\n"); - out.push_str(metadata.to_string().as_slice()); - } - None => {} - } - - try!(File::create(&loc).write_str(out.as_slice())); - Ok(()) -} - -fn emit_package(dep: &toml::TomlTable, out: &mut String) { - out.push_str(format!("name = {}\n", lookup(dep, "name")).as_slice()); - out.push_str(format!("version = {}\n", lookup(dep, "version")).as_slice()); - - if dep.contains_key(&"source".to_string()) { - out.push_str(format!("source = {}\n", lookup(dep, "source")).as_slice()); - } - - if let Some(ref s) = dep.find(&"dependencies".to_string()) { - let slice = s.as_slice().unwrap(); - - if !slice.is_empty() { - out.push_str("dependencies = [\n"); - - for child in s.as_slice().unwrap().iter() { - out.push_str(format!(" {},\n", child).as_slice()); - } - - out.push_str("]\n"); - } - out.push_str("\n"); - } -} - -fn lookup<'a>(table: &'a toml::TomlTable, key: &str) -> &'a toml::Value { - table.find(&key.to_string()).expect(format!("Didn't find {}", key).as_slice()) -} diff --git a/src/cargo/ops/lockfile.rs b/src/cargo/ops/lockfile.rs new file mode 100644 index 000000000..24c60cd42 --- /dev/null +++ b/src/cargo/ops/lockfile.rs @@ -0,0 +1,101 @@ +use std::collections::HashSet; +use std::io::File; + +use serialize::{Encodable, Decodable}; +use toml::{mod, Encoder}; + +use core::registry::PackageRegistry; +use core::{MultiShell, Source, Resolve, resolver, Package, SourceId}; +use core::PackageId; +use sources::{PathSource}; +use util::config::{Config}; +use util::{CargoResult, human}; +use util::toml as cargo_toml; + +pub fn load_pkg_lockfile(pkg: &Package) -> CargoResult> { + let lockfile = pkg.get_manifest_path().dir_path().join("Cargo.lock"); + let source_id = pkg.get_package_id().get_source_id(); + load_lockfile(&lockfile, source_id) +} + +pub fn load_lockfile(path: &Path, sid: &SourceId) -> CargoResult> { + // If there is no lockfile, return none. + let mut f = match File::open(path) { + Ok(f) => f, + Err(_) => return Ok(None) + }; + + let s = try!(f.read_to_string()); + + let table = toml::Table(try!(cargo_toml::parse(s.as_slice(), path))); + let mut d = toml::Decoder::new(table); + let v: resolver::EncodableResolve = Decodable::decode(&mut d).unwrap(); + Ok(Some(try!(v.to_resolve(sid)))) +} + +pub fn write_pkg_lockfile(pkg: &Package, resolve: &Resolve) -> CargoResult<()> { + let loc = pkg.get_root().join("Cargo.lock"); + write_lockfile(&loc, resolve) +} + +pub fn write_lockfile(dst: &Path, resolve: &Resolve) -> CargoResult<()> { + let mut e = Encoder::new(); + resolve.encode(&mut e).unwrap(); + + let mut out = String::new(); + + // Note that we do not use e.toml.to_string() as we want to control the + // exact format the toml is in to ensure pretty diffs between updates to the + // lockfile. + let root = e.toml.find(&"root".to_string()).unwrap(); + + out.push_str("[root]\n"); + emit_package(root.as_table().unwrap(), &mut out); + + let deps = e.toml.find(&"package".to_string()).unwrap().as_slice().unwrap(); + for dep in deps.iter() { + let dep = dep.as_table().unwrap(); + + out.push_str("[[package]]\n"); + emit_package(dep, &mut out); + } + + match e.toml.find(&"metadata".to_string()) { + Some(metadata) => { + out.push_str("[metadata]\n"); + out.push_str(metadata.to_string().as_slice()); + } + None => {} + } + + try!(File::create(dst).write_str(out.as_slice())); + Ok(()) +} + +fn emit_package(dep: &toml::TomlTable, out: &mut String) { + out.push_str(format!("name = {}\n", lookup(dep, "name")).as_slice()); + out.push_str(format!("version = {}\n", lookup(dep, "version")).as_slice()); + + if dep.contains_key(&"source".to_string()) { + out.push_str(format!("source = {}\n", lookup(dep, "source")).as_slice()); + } + + if let Some(ref s) = dep.find(&"dependencies".to_string()) { + let slice = s.as_slice().unwrap(); + + if !slice.is_empty() { + out.push_str("dependencies = [\n"); + + for child in s.as_slice().unwrap().iter() { + out.push_str(format!(" {},\n", child).as_slice()); + } + + out.push_str("]\n"); + } + out.push_str("\n"); + } +} + +fn lookup<'a>(table: &'a toml::TomlTable, key: &str) -> &'a toml::Value { + table.find(&key.to_string()).expect(format!("Didn't find {}", key).as_slice()) +} diff --git a/src/cargo/ops/mod.rs b/src/cargo/ops/mod.rs index 64e811bbe..5176ce75f 100644 --- a/src/cargo/ops/mod.rs +++ b/src/cargo/ops/mod.rs @@ -8,27 +8,32 @@ pub use self::cargo_rustc::{PlatformPlugin, PlatformPluginAndTarget}; pub use self::cargo_run::run; pub use self::cargo_new::{new, NewOptions}; pub use self::cargo_doc::{doc, DocOptions}; -pub use self::cargo_generate_lockfile::{generate_lockfile, write_resolve}; -pub use self::cargo_generate_lockfile::{update_lockfile, load_lockfile}; +pub use self::cargo_generate_lockfile::{generate_lockfile}; +pub use self::cargo_generate_lockfile::{update_lockfile}; pub use self::cargo_generate_lockfile::UpdateOptions; +pub use self::lockfile::{load_lockfile, load_pkg_lockfile}; +pub use self::lockfile::{write_lockfile, write_pkg_lockfile}; pub use self::cargo_test::{run_tests, run_benches, TestOptions}; pub use self::cargo_package::package; pub use self::registry::{publish, registry_configuration, RegistryConfig}; pub use self::registry::{registry_login, http_proxy, http_handle}; pub use self::registry::{modify_owners, yank}; -pub use self::cargo_fetch::{fetch, resolve_and_fetch}; +pub use self::cargo_fetch::{fetch}; pub use self::cargo_pkgid::pkgid; +pub use self::resolve::{resolve_pkg, resolve_with_previous}; mod cargo_clean; mod cargo_compile; -mod cargo_read_manifest; -mod cargo_rustc; -mod cargo_run; -mod cargo_new; mod cargo_doc; +mod cargo_fetch; mod cargo_generate_lockfile; -mod cargo_test; +mod cargo_new; mod cargo_package; -mod cargo_fetch; mod cargo_pkgid; +mod cargo_read_manifest; +mod cargo_run; +mod cargo_rustc; +mod cargo_test; +mod lockfile; mod registry; +mod resolve; diff --git a/src/cargo/ops/resolve.rs b/src/cargo/ops/resolve.rs new file mode 100644 index 000000000..c9ad81c05 --- /dev/null +++ b/src/cargo/ops/resolve.rs @@ -0,0 +1,56 @@ +use std::collections::HashMap; + +use semver::VersionReq; + +use core::{MultiShell, Package}; +use core::registry::PackageRegistry; +use core::resolver::{mod, Resolve}; +use core::source::Source; +use ops; +use sources::PathSource; +use util::{CargoResult, Config}; +use util::profile; + +/// Resolve all dependencies for the specified `package` using the previous +/// lockfile as a guide if present. +/// +/// This function will also generate a write the result of resolution as a new +/// lockfile. +pub fn resolve_pkg(registry: &mut PackageRegistry, package: &Package) + -> CargoResult { + let prev = try!(ops::load_pkg_lockfile(package)); + let resolve = try!(resolve_with_previous(registry, package, prev.as_ref())); + try!(ops::write_pkg_lockfile(package, &resolve)); + Ok(resolve) +} + +/// Resolve all dependencies for a package using an optional prevoius instance +/// of resolve to guide the resolution process. +/// +/// The previous resolve normally comes from a lockfile. This function does not +/// read or write lockfiles from the filesystem. +pub fn resolve_with_previous(registry: &mut PackageRegistry, + package: &Package, + previous: Option<&Resolve>) + -> CargoResult { + let root = package.get_package_id().get_source_id().clone(); + try!(registry.add_sources(&[root])); + + match previous { + Some(r) => { + let v = r.iter().map(|p| p.get_source_id().clone()) + .collect::>(); + try!(registry.add_sources(v.as_slice())); + } + None => {} + }; + + let mut resolved = try!(resolver::resolve(package.get_summary(), + resolver::ResolveEverything, + registry)); + match previous { + Some(r) => resolved.copy_metadata(previous), + None => {} + } + Ok(resolved) +} -- 2.30.2